#!/bin/bash
# ThingsBoard 恢复脚本
# 版本: 2.0.0 (优化简化版 - 仅支持直接恢复)
#
# 优化说明:
# 本脚本采用模块化设计，分为以下几个主要部分：
# 1. 初始化 - 加载配置、设置环境变量
# 2. 辅助函数 - 通用功能函数、S3操作函数等
# 3. 恢复流程函数 - 分步骤执行恢复过程
# 4. 主程序入口 - 解析参数，调用相应的恢复流程
#
# 使用方法详见 --help 选项

# ========== 1. 初始化部分 ==========

# 设置脚本名称
SCRIPT_NAME="restore"

# 记录当前工作目录
CURRENT_DIR=$(pwd)

# 获取脚本所在目录的绝对路径
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)

# 根据脚本目录计算项目根目录路径
if [[ "$SCRIPT_DIR" == */scripts/restore ]]; then
  # 如果脚本位于scripts/restore目录中
  PROJECT_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd)
elif [[ "$SCRIPT_DIR" == */scripts/* ]]; then
  # 如果脚本位于scripts目录或其他子目录中
  PROJECT_ROOT=$(echo "$SCRIPT_DIR" | sed 's|\(.*\)/scripts.*|\1|')
else
  # 否则假设当前目录是项目根目录
  PROJECT_ROOT=$(pwd)
fi

# 配置文件路径
CONFIG_FILE="$PROJECT_ROOT/config/backup_env.conf"

# 检查配置文件是否存在
if [ ! -f "$CONFIG_FILE" ]; then
  echo "错误: 配置文件不存在: $CONFIG_FILE"
  exit 1
fi

# 导入共用函数 - 使用绝对路径
COMMON_SCRIPT="$PROJECT_ROOT/scripts/common.sh"
if [ -f "$COMMON_SCRIPT" ]; then
  source "$COMMON_SCRIPT"
else
  echo "错误: 共用脚本不存在: $COMMON_SCRIPT"
  exit 1
fi

# ========== 2. 辅助函数 ==========

# 创建WAL-G配置文件
create_walg_config() {
  local prefix=$1
  cat > "/root/.walg.json" <<EOF
{
  "WALG_S3_PREFIX": "s3://${MINIO_BUCKET}/${prefix}",
  "AWS_ACCESS_KEY_ID": "${MINIO_USER}",
  "AWS_SECRET_ACCESS_KEY": "${MINIO_PASSWORD}",
  "AWS_ENDPOINT": "http://${MINIO_HOST}:${MINIO_PORT}",
  "AWS_S3_FORCE_PATH_STYLE": "true",
  "PGHOST": "localhost",
  "PGPORT": "5432",
  "PGUSER": "$POSTGRES_SUPERUSER",
  "PGPASSWORD": "$POSTGRES_PASSWORD",
  "PGDATABASE": "$THINGSBOARD_DATABASE_NAME",
  "WALG_COMPRESSION_METHOD": "lz4"
}
EOF
  log_info "WAL-G配置文件已创建: /root/.walg.json (前缀: ${prefix})"
}

# 默认创建配置，使用全量备份前缀
create_walg_config "${MINIO_FULL_PREFIX}"

# 检查和安装所需工具
check_and_install_tools() {
  # 检查s3cmd是否安装
  if ! command -v s3cmd &> /dev/null; then
      log_warn "s3cmd未安装，正在安装..."
      apt-get update && apt-get install -y s3cmd || log_error "s3cmd安装失败"
  fi

  # 检查jq是否安装
  if ! command -v jq &> /dev/null; then
      log_warn "jq未安装，正在安装..."
      apt-get update && apt-get install -y jq || log_error "jq安装失败"
  fi
}

# 创建临时S3配置文件
create_temp_s3cfg() {
  local temp_s3cfg=$(mktemp)
  cat > "$temp_s3cfg" << EOF
[default]
access_key = $MINIO_USER
secret_key = $MINIO_PASSWORD
host_base = $MINIO_HOST:$MINIO_PORT
host_bucket = $MINIO_HOST:$MINIO_PORT/%(bucket)s
use_https = False
signature_v2 = False
# 使用路径风格访问
use_path_style = True
# 禁用DNS样式的存储桶检查
check_ssl_certificate = False
check_ssl_hostname = False
EOF
  echo "$temp_s3cfg"
}

# 从MinIO下载文件
download_from_minio() {
    local s3_path="$1"
    local local_file="$2"

    log_info "从MinIO下载: $s3_path 到 $local_file"

    # 创建本地目录
    mkdir -p "$(dirname "$local_file")"

    # 创建临时s3cfg配置文件
    TEMP_S3CFG=$(create_temp_s3cfg)

    # 下载文件
    s3cmd -c "$TEMP_S3CFG" get "s3://$MINIO_BUCKET/$s3_path" "$local_file" > /dev/null 2>&1
    DOWNLOAD_STATUS=$?

    # 清理临时配置文件
    rm -f "$TEMP_S3CFG"

    if [ $DOWNLOAD_STATUS -eq 0 ]; then
        log_success "文件 $s3_path 下载成功"
        return 0
    else
        log_error "文件 $s3_path 下载失败"
        return 1
    fi
}

# 创建最小元数据文件
create_minimal_metadata() {
    log_info "创建最小元数据文件..."
    mkdir -p "$TEMP_RESTORE_DIR"
    cat > "$TEMP_RESTORE_DIR/metadata.json" <<EOF
{
  "backup_id": "${BACKUP_NAME}",
  "backup_type": "${BACKUP_TYPE}",
  "timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
  "postgresql": {
    "walg_backup_name": "${BACKUP_NAME}"
  }
}
EOF
    log_info "已创建最小元数据文件"

    if [ -f "$TEMP_RESTORE_DIR/metadata.json" ]; then
        log_success "元数据文件创建成功"
    else
        log_error "元数据文件创建失败"
        handle_error 15 "无法创建元数据文件"
    fi
}

# 使用方法显示
usage() {
    echo "使用方法: $0 [-b <备份路径>] [-n <备份名称>] [-f] [-c] [-d] [-s] [-t <时间点>]"
    echo "  -b <备份路径>     指定要恢复的备份路径（本地文件系统路径）"
    echo "  -n <备份名称>     指定要从MinIO恢复的备份名称"
    echo "  -f               不恢复Nginx配置和静态文件"
    echo "  -c               不恢复ThingsBoard配置文件"
    echo "  -d               不恢复ThingsBoard数据文件"
    echo "  -s               不恢复ThingBelt静态资源文件"
    echo "  -t <时间点>       指定恢复到的时间点 (格式: YYYY-MM-DDTHH:MM:SSZ)"
    echo "  -h               显示此帮助"
    exit 1
}

# ========== 4. 主程序入口 ==========

# 解析参数
BACKUP_PATH=""
RESTORE_CONFIG=true
RESTORE_DATA=true
RESTORE_STATIC=true
RESTORE_NGINX=true
PITR_TIMESTAMP=""
BACKUP_NAME=""

# 解析命令行参数
while getopts "b:n:fcdst:h" opt; do
    case $opt in
        b) BACKUP_PATH="$OPTARG" ;;
        n) BACKUP_NAME="$OPTARG" ;;
        f) RESTORE_NGINX=false ;;
        c) RESTORE_CONFIG=false ;;
        d) RESTORE_DATA=false ;;
        s) RESTORE_STATIC=false ;;
        t) PITR_TIMESTAMP="$OPTARG" ;;
        h) usage ;;
        *) usage ;;
    esac
done

# 参数检查
if [ -z "$BACKUP_PATH" ] && [ -z "$BACKUP_NAME" ]; then
    log_error "未指定备份路径或备份名称"
    usage
fi

# 检查备份路径格式
if [ -n "$BACKUP_PATH" ] && [ ! -d "$BACKUP_PATH" ]; then
    # 检查是不是备份ID格式
    if [[ "$BACKUP_PATH" =~ ^(full|diff)_[0-9]{8}_[0-9]{6}$ ]]; then
        log_info "检测到备份ID格式，将作为备份名称处理: $BACKUP_PATH"
        BACKUP_NAME="$BACKUP_PATH"
        BACKUP_PATH=""
    else
        handle_error 1 "指定的备份路径不存在: $BACKUP_PATH"
    fi
fi

# 准备时间点恢复标志
if [ -n "$PITR_TIMESTAMP" ]; then
    PITR_FLAG="$PITR_TIMESTAMP"
    log_info "将执行时间点恢复，时间点: $PITR_TIMESTAMP"
else
    PITR_FLAG=""
fi

# 执行恢复操作
log_info "================== ThingsBoard恢复操作开始 =================="
log_info "使用直接恢复模式"

# 备份源准备函数
prepare_backup_source() {
    local step=$(next_step)
    log_info "准备备份源..." "$step"

    # 如果指定了备份名称，检查MinIO中是否存在该备份
    if [ -n "$BACKUP_NAME" ]; then
        log_info "检查MinIO中是否存在备份 $BACKUP_NAME..." "$step"

        # 创建临时s3cfg配置文件
        TEMP_S3CFG=$(create_temp_s3cfg)

        # 检查索引文件
        local found_backup=false
        local index_file_path="thingsboard-backup/index/${BACKUP_NAME}.json"

        if s3cmd -c "$TEMP_S3CFG" info "s3://$MINIO_BUCKET/$index_file_path" &>/dev/null; then
            log_info "找到备份索引文件: $index_file_path" "$step"
            found_backup=true

            # 从索引文件获取元数据
            local temp_index_file=$(mktemp)
            s3cmd -c "$TEMP_S3CFG" get "s3://$MINIO_BUCKET/$index_file_path" "$temp_index_file" > /dev/null

            # 解析元数据
            BACKUP_TYPE=$(jq -r '.backup_type' "$temp_index_file")
            WALG_BACKUP_NAME=$(jq -r '.walg_backup_name' "$temp_index_file")

            # 确定WAL-G前缀
            if [ "$BACKUP_TYPE" = "full" ]; then
                WAL_G_PREFIX="${MINIO_FULL_PREFIX}"
                log_info "备份类型为全量备份" "$step"
            else
                WAL_G_PREFIX="${MINIO_DIFF_PREFIX}"
                log_info "备份类型为增量备份" "$step"
            fi

            # 清理临时文件
            rm -f "$temp_index_file"
        else
            # 尝试直接从WAL-G备份列表中查找
            log_info "未找到索引文件，检查WAL-G备份列表..." "$step"

            # 先检查全量备份
            create_walg_config "${MINIO_FULL_PREFIX}"
            export WALG_S3_PREFIX="s3://${MINIO_BUCKET}/${MINIO_FULL_PREFIX}"
            export AWS_ACCESS_KEY_ID="${MINIO_USER}"
            export AWS_SECRET_ACCESS_KEY="${MINIO_PASSWORD}"
            export AWS_ENDPOINT="http://${MINIO_HOST}:${MINIO_PORT}"
            export AWS_S3_FORCE_PATH_STYLE="true"

            local backup_exists=$($(which wal-g) backup-list 2>/dev/null | grep -c "$BACKUP_NAME" || echo "0")

            if [ "$backup_exists" -eq "1" ]; then
                log_info "在全量备份中找到备份: $BACKUP_NAME" "$step"
                WAL_G_PREFIX="${MINIO_FULL_PREFIX}"
                BACKUP_TYPE="full"
                WALG_BACKUP_NAME="$BACKUP_NAME"
                found_backup=true
            else
                # 再检查增量备份
                create_walg_config "${MINIO_DIFF_PREFIX}"
                export WALG_S3_PREFIX="s3://${MINIO_BUCKET}/${MINIO_DIFF_PREFIX}"

                backup_exists=$($(which wal-g) backup-list 2>/dev/null | grep -c "$BACKUP_NAME" || echo "0")

                if [ "$backup_exists" -eq "1" ]; then
                    log_info "在增量备份中找到备份: $BACKUP_NAME" "$step"
                    WAL_G_PREFIX="${MINIO_DIFF_PREFIX}"
                    BACKUP_TYPE="differential"
                    WALG_BACKUP_NAME="$BACKUP_NAME"
                    found_backup=true
                fi
            fi
        fi

        # 清理临时配置文件
        rm -f "$TEMP_S3CFG"

        if [ "$found_backup" = "false" ]; then
            handle_error 2 "指定的备份名称在MinIO中不存在: $BACKUP_NAME" "$step"
        fi

        # 创建临时恢复目录
        TEMP_RESTORE_DIR="$RECOVERY_TEMP_DIR/$BACKUP_NAME"
        mkdir -p "$TEMP_RESTORE_DIR"

        # 更新WAL-G配置文件
        create_walg_config "$WAL_G_PREFIX"

        # 设置元数据
        if [ -z "$WALG_BACKUP_NAME" ]; then
            WALG_BACKUP_NAME="$BACKUP_NAME"
        fi

        RESTORE_BACKUP_NAME="$WALG_BACKUP_NAME"
        BACKUP_ID="minio_backup_$BACKUP_NAME"
        BACKUP_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

        log_info "准备从MinIO备份 $BACKUP_NAME ($BACKUP_TYPE) 恢复系统" "$step"
        log_success "备份源准备完成" "$step"
        return 0
    fi

    # 如果使用本地备份路径
    if [ -n "$BACKUP_PATH" ]; then
        log_info "使用本地备份路径: $BACKUP_PATH" "$step"

        # 检查元数据文件
        local metadata_file="$BACKUP_PATH/metadata.json"
        if [ ! -f "$metadata_file" ]; then
            handle_error 2 "备份元数据文件不存在: $metadata_file" "$step"
        fi

        # 解析元数据
        BACKUP_ID=$(jq -r '.backup_id' "$metadata_file")
        BACKUP_TYPE=$(jq -r '.backup_type' "$metadata_file")
        BACKUP_TIMESTAMP=$(jq -r '.timestamp' "$metadata_file")
        WALG_BACKUP_NAME=$(jq -r '.postgresql.walg_backup_name' "$metadata_file")

        # 确定WAL-G前缀
        if [ "$BACKUP_TYPE" = "full" ]; then
            WAL_G_PREFIX="${MINIO_FULL_PREFIX}"
        else
            WAL_G_PREFIX="${MINIO_DIFF_PREFIX}"
        fi

        # 更新WAL-G配置
        create_walg_config "$WAL_G_PREFIX"

        # 设置恢复备份名称
        RESTORE_BACKUP_NAME="$WALG_BACKUP_NAME"

        log_info "准备从备份 $BACKUP_ID ($BACKUP_TYPE) 恢复系统，备份时间: $BACKUP_TIMESTAMP" "$step"
        log_success "备份元数据读取成功" "$step"
        return 0
    fi

    # 如果没有指定备份路径或备份名称
    handle_error 1 "未指定备份路径或备份名称" "$step"
}

# 停止所有服务
stop_services() {
    local step=$(next_step)
    log_info "停止所有服务..." "$step"

    # 停止ThingsBoard
    stop_thingsboard

    # 停止PostgreSQL
    if [ "$USE_DOCKER" = "true" ]; then
        log_info "停止PostgreSQL容器..." "$step"
        docker stop "$POSTGRES_CONTAINER_NAME" || log_warn "PostgreSQL容器已经停止" "$step"
    else
        log_info "停止PostgreSQL服务..." "$step"
        systemctl stop postgresql || log_warn "PostgreSQL服务已经停止" "$step"
    fi

    # 停止Nginx
    if [ "$USE_DOCKER" = "true" ] && [ "$RESTORE_NGINX" = "true" ]; then
        log_info "停止Nginx容器..." "$step"
        docker stop "$NGINX_CONTAINER_NAME" || log_warn "Nginx容器已经停止" "$step"
    fi

    log_success "所有服务已停止" "$step"
}

# 数据库恢复函数 - 使用WAL-G直接恢复数据
restore_postgresql_data() {
  local step=$(next_step)
  local data_dir="$1"
  local backup_name="$2"
  local pitr_flag="$3"

  log_info "准备恢复PostgreSQL数据..." "$step"

  # 停止PostgreSQL服务
  if [ "$USE_DOCKER" = "true" ]; then
    log_info "停止PostgreSQL容器..." "$step"
    docker stop "$POSTGRES_CONTAINER_NAME" || log_warn "PostgreSQL容器已经停止" "$step"
  else
    log_info "停止PostgreSQL服务..." "$step"
    systemctl stop postgresql || handle_error 5 "无法停止PostgreSQL服务" "$step"
  fi

  # 备份现有数据目录
  if [ -d "$data_dir" ]; then
    local backup_dir="${data_dir}.bak_$(date +%Y%m%d_%H%M%S)"
    log_info "备份当前数据目录到: $backup_dir" "$step"
    mv "$data_dir" "$backup_dir" || handle_error 6 "无法备份当前PostgreSQL数据目录" "$step"
  fi

  # 创建新的数据目录
  mkdir -p "$data_dir"
  chmod 700 "$data_dir"

  # 确保WAL-G命令可用
  local walg_path=$(which wal-g)
  if [ -z "$walg_path" ]; then
    handle_error 7 "未找到wal-g命令，请先安装WAL-G" "$step"
  fi

  # 执行WAL-G恢复
  log_info "使用WAL-G恢复数据..." "$step"

  if [ -n "$pitr_flag" ]; then
    # 时间点恢复
    log_info "执行时间点恢复，时间点: $pitr_flag" "$step"
    wal-g backup-fetch "$data_dir" LATEST
  else
    # 指定备份恢复
    log_info "恢复备份: $backup_name" "$step"
    wal-g backup-fetch "$data_dir" "$backup_name"
  fi

  local restore_status=$?
  if [ $restore_status -ne 0 ]; then
    handle_error 8 "WAL-G恢复失败，退出代码: $restore_status" "$step"
  fi

  # 创建恢复信号文件
  touch "${data_dir}/recovery.signal"
  chmod 600 "${data_dir}/recovery.signal"

  # 设置适当的权限
  if [ "$USE_DOCKER" = "true" ]; then
    # Docker环境下设置权限
    chown -R 999:999 "$data_dir"  # 999是PostgreSQL容器中postgres用户的UID
  else
    # 本地PostgreSQL需要正确的权限
    chown -R postgres:postgres "$data_dir"
  fi

  log_success "PostgreSQL数据恢复完成" "$step"
  return 0
}

# 启动数据库服务
start_postgresql() {
  local step=$(next_step)
  log_info "启动PostgreSQL服务..." "$step"

  if [ "$USE_DOCKER" = "true" ]; then
    log_info "启动PostgreSQL容器..." "$step"
    docker start "$POSTGRES_CONTAINER_NAME" || handle_error 50 "无法启动PostgreSQL容器" "$step"

    # 等待PostgreSQL启动
    sleep 10
    if ! docker ps | grep -q "$POSTGRES_CONTAINER_NAME"; then
      handle_error 50 "PostgreSQL容器启动失败" "$step"
    fi
  else
    log_info "启动PostgreSQL服务..." "$step"
    systemctl start postgresql || handle_error 51 "无法启动PostgreSQL服务" "$step"

    if ! systemctl is-active postgresql &>/dev/null; then
      handle_error 51 "PostgreSQL服务启动失败" "$step"
    fi
  fi

  # 检查PostgreSQL连接
  check_postgres_available

  log_success "PostgreSQL服务已启动" "$step"
}

# ThingsBoard文件恢复函数
restore_thingsboard_files() {
    local step=$(next_step)
    local backup_name="$1"

    log_info "恢复ThingsBoard文件..." "$step"

    # 创建必要的子目录结构
    mkdir -p "$TEMP_RESTORE_DIR/thingsboard"
    mkdir -p "$TEMP_RESTORE_DIR/nginx"

    # 恢复原始的S3路径前缀，ThingsBoard文件在thingsboard-backup目录下
    # 而不是在WAL-G的basebackups_005目录下
    local s3_prefix="thingsboard-backup/${backup_name}"

    log_info "使用ThingsBoard文件的S3路径前缀: ${s3_prefix}" "$step"

    # 根据恢复选项恢复相应文件
    if [ "$RESTORE_CONFIG" = "true" ]; then
        restore_thingsboard_config "$s3_prefix" "$step"
    else
        log_info "跳过ThingsBoard配置文件恢复" "$step"
    fi

    if [ "$RESTORE_DATA" = "true" ]; then
        restore_thingsboard_data "$s3_prefix" "$step"
    else
        log_info "跳过ThingsBoard数据文件恢复" "$step"
    fi

    if [ "$RESTORE_STATIC" = "true" ]; then
        restore_thingbelt_static "$s3_prefix" "$step"
    else
        log_info "跳过ThingBelt静态资源文件恢复" "$step"
    fi

    if [ "$RESTORE_NGINX" = "true" ]; then
        restore_nginx_files "$s3_prefix" "$step"
    else
        log_info "跳过Nginx配置和静态文件恢复" "$step"
    fi

    log_success "ThingsBoard文件恢复操作完成" "$step"
}

# 恢复ThingsBoard配置文件
restore_thingsboard_config() {
    local s3_prefix="$1"
    local step="$2"

    log_info "恢复ThingsBoard配置文件..." "$step"
    local tb_config_s3_path="${s3_prefix}/tb_config.tar.gz"
    local tb_config_local_path="$TEMP_RESTORE_DIR/thingsboard/tb_config.tar.gz"

    # 下载配置文件
    download_from_minio "$tb_config_s3_path" "$tb_config_local_path"

    if [ -f "$tb_config_local_path" ]; then
        # 解压配置文件
        log_info "解压ThingsBoard配置文件..." "$step"
        mkdir -p "$(dirname "$TB_CONFIG_DIR")"
        tar -xzf "$tb_config_local_path" -C / || log_warn "ThingsBoard配置文件解压失败" "$step"
        log_success "ThingsBoard配置文件恢复完成" "$step"
    else
        log_warn "未找到ThingsBoard配置文件备份" "$step"
    fi
}

# 恢复ThingsBoard数据文件
restore_thingsboard_data() {
    local s3_prefix="$1"
    local step="$2"

    log_info "恢复ThingsBoard数据文件..." "$step"
    local tb_data_s3_path="${s3_prefix}/tb_data.tar.gz"
    local tb_data_local_path="$TEMP_RESTORE_DIR/thingsboard/tb_data.tar.gz"

    # 下载数据文件
    download_from_minio "$tb_data_s3_path" "$tb_data_local_path"

    if [ -f "$tb_data_local_path" ]; then
        # 解压数据文件
        log_info "解压ThingsBoard数据文件..." "$step"
        mkdir -p "$(dirname "$TB_DATA_DIR")"
        tar -xzf "$tb_data_local_path" -C / || log_warn "ThingsBoard数据文件解压失败" "$step"
        log_success "ThingsBoard数据文件恢复完成" "$step"
    else
        log_warn "未找到ThingsBoard数据文件备份" "$step"
    fi
}

# 恢复ThingBelt静态资源
restore_thingbelt_static() {
    local s3_prefix="$1"
    local step="$2"

    log_info "恢复ThingBelt静态资源文件..." "$step"
    local tb_static_s3_path="${s3_prefix}/thingbelt_static.tar.gz"
    local tb_static_local_path="$TEMP_RESTORE_DIR/thingsboard/thingbelt_static.tar.gz"

    # 下载静态资源
    download_from_minio "$tb_static_s3_path" "$tb_static_local_path"

    if [ -f "$tb_static_local_path" ]; then
        # 解压静态资源
        log_info "解压ThingBelt静态资源文件..." "$step"
        mkdir -p "$(dirname "$THINGBELT_STATIC_DIR")"
        tar -xzf "$tb_static_local_path" -C / || log_warn "ThingBelt静态资源文件解压失败" "$step"
        log_success "ThingBelt静态资源文件恢复完成" "$step"
    else
        log_warn "未找到ThingBelt静态资源文件备份" "$step"
    fi
}

# 恢复Nginx文件
restore_nginx_files() {
    local s3_prefix="$1"
    local step="$2"

    log_info "恢复Nginx配置和静态文件..." "$step"
    local nginx_s3_path="${s3_prefix}/nginx_data.tar.gz"
    local nginx_local_path="$TEMP_RESTORE_DIR/nginx/nginx_data.tar.gz"

    # 下载Nginx文件
    download_from_minio "$nginx_s3_path" "$nginx_local_path"

    if [ -f "$nginx_local_path" ]; then
        # 解压Nginx文件
        log_info "解压Nginx配置和静态文件..." "$step"
        mkdir -p "$(dirname "$NGINX_DATA_DIR")"
        tar -xzf "$nginx_local_path" -C / || log_warn "Nginx配置和静态文件解压失败" "$step"
        log_success "Nginx配置和静态文件恢复完成" "$step"
    else
        log_warn "未找到Nginx配置和静态文件备份" "$step"
    fi
}

# 恢复本地ThingsBoard文件
restore_local_thingsboard_files() {
    local step=$(next_step)
    log_info "从本地备份恢复ThingsBoard文件..." "$step"

    if [ "$RESTORE_CONFIG" = "true" ]; then
        log_info "恢复ThingsBoard配置文件..." "$step"
        local tb_config_backup="$BACKUP_PATH/thingsboard/tb_config.tar.gz"
        if [ -f "$tb_config_backup" ]; then
            tar -xzf "$tb_config_backup" -C /
            log_success "ThingsBoard配置文件已恢复" "$step"
        else
            log_warn "ThingsBoard配置备份文件不存在: $tb_config_backup" "$step"
        fi
    fi

    if [ "$RESTORE_DATA" = "true" ]; then
        log_info "恢复ThingsBoard数据文件..." "$step"
        local tb_data_backup="$BACKUP_PATH/thingsboard/tb_data.tar.gz"
        if [ -f "$tb_data_backup" ]; then
            tar -xzf "$tb_data_backup" -C /
            log_success "ThingsBoard数据文件已恢复" "$step"
        else
            log_warn "ThingsBoard数据备份文件不存在: $tb_data_backup" "$step"
        fi
    fi

    if [ "$RESTORE_STATIC" = "true" ]; then
        log_info "恢复ThingBelt静态资源文件..." "$step"
        local static_backup="$BACKUP_PATH/thingsboard/thingbelt_static.tar.gz"
        if [ -f "$static_backup" ]; then
            tar -xzf "$static_backup" -C /
            log_success "ThingBelt静态资源文件已恢复" "$step"
        else
            log_warn "ThingBelt静态资源备份文件不存在: $static_backup" "$step"
        fi
    fi

    if [ "$RESTORE_NGINX" = "true" ]; then
        log_info "恢复Nginx配置和静态文件..." "$step"
        local nginx_backup="$BACKUP_PATH/nginx/nginx_data.tar.gz"
        if [ -f "$nginx_backup" ]; then
            tar -xzf "$nginx_backup" -C /
            log_success "Nginx配置和静态文件已恢复" "$step"
        else
            log_warn "Nginx备份文件不存在: $nginx_backup" "$step"
        fi
    fi

    log_success "本地ThingsBoard文件恢复完成" "$step"
}

# 测试并启动服务
start_services() {
    local step=$(next_step)
    log_info "启动服务..." "$step"

    # 启动PostgreSQL (如已在其他函数中启动，则跳过)
    if ! check_postgres_available; then
        start_postgresql
    fi

    # 启动ThingsBoard
    log_info "启动ThingsBoard服务..." "$step"
    start_thingsboard

    # 启动Nginx (如果需要)
    if [ "$RESTORE_NGINX" = "true" ] && [ "$USE_DOCKER" = "true" ]; then
        log_info "启动Nginx容器..." "$step"
        docker start "$NGINX_CONTAINER_NAME" || log_warn "无法启动Nginx容器" "$step"
    fi

    # 验证服务状态
    sleep 10
    if ! check_thingsboard_service; then
        log_warn "ThingsBoard服务启动失败，尝试再次启动..." "$step"
        start_thingsboard
        sleep 10
        if ! check_thingsboard_service; then
            log_warn "ThingsBoard服务可能未正确启动，请手动检查" "$step"
        fi
    fi

    log_success "服务启动完成" "$step"
}

# 验证恢复结果
verify_restore() {
    local step=$(next_step)
    log_info "验证恢复结果..." "$step"

    # 检查PostgreSQL连接
    if ! check_postgres_available; then
        log_error "PostgreSQL数据库无法连接" "$step"
        return 1
    fi

    # 检查ThingsBoard服务
    if ! check_thingsboard_service; then
        log_warn "ThingsBoard服务未正常运行" "$step"
        return 1
    fi

    log_success "恢复验证通过" "$step"
    return 0
}

# 完整恢复流程
perform_restore() {
    log_info "开始执行完整恢复流程..."

    # 1. 准备环境
    check_and_install_tools

    # 2. 根据参数确定备份来源和名称
    prepare_backup_source

    # 3. 停止服务
    stop_services

    # 4. 确定数据目录
    if [ "$USE_DOCKER" = "true" ]; then
        CURRENT_DATA_DIR="/home/docker/postgresql/data"
    else
        CURRENT_DATA_DIR="$POSTGRES_DATA_DIR"
    fi

    # 5. 恢复PostgreSQL数据
    restore_postgresql_data "$CURRENT_DATA_DIR" "$RESTORE_BACKUP_NAME" "$PITR_FLAG"

    # 6. 恢复ThingsBoard文件
    if [ -n "$BACKUP_NAME" ]; then
        restore_thingsboard_files "$BACKUP_NAME"
    elif [ -n "$BACKUP_PATH" ]; then
        restore_local_thingsboard_files
    fi

    # 7. 启动服务
    start_services

    # 8. 验证恢复结果
    verify_restore

    # 9. 清理工作
    log_info "保留临时文件以便需要时回滚"

    log_success "恢复过程完成！"
    log_info "恢复来源: $([ -n "$BACKUP_PATH" ] && echo "本地备份 $BACKUP_ID" || echo "MinIO备份 $BACKUP_NAME") ($BACKUP_TYPE)"
    log_info "备份时间: $BACKUP_TIMESTAMP"
    if [ -n "$PITR_TIMESTAMP" ]; then
        log_info "恢复到时间点: $PITR_TIMESTAMP"
    fi
    log_info "请验证系统功能是否正常"
}

# 执行恢复操作
perform_restore

log_info "================== ThingsBoard恢复操作完成 =================="
exit 0

